/* Filename: server.c
 * No.151-172.流媒体广播项目
 * Description: 服务端
 * Last modified: humble 2020-09-25 15:17:49
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/if.h>
#include <fcntl.h>
#include <syslog.h>

#include <proto.h>

#include "server_conf.h"
#include "medialib.h"
#include "thr_list.h"

struct server_conf_st server_conf = {
    .rcvport = DEFAULT_RCVPORT,
    .mgroup = DEFAULE_MGROUP,
    .media_dir = DEFAULT_MEDIADIR,
    .runmode = RUN_DAEMON,
    .ifname = DEFAULT_IF
};

int serversd;
struct sockaddr_in sndaddr;

static void usage(void)
{
#define USAGE "Usage:\n\
  ./server\n\
    -M <224.2.2.2> 指定多拨组\n\
    -P <1989>      指定接收端口\n\
    -F             前台运行\n\
    -D             指定媒体库位置\n\
    -I             指定网络设备\n\
    -H             显示帮助\n\
    \n"
    printf(USAGE);
}

static void daemon_exit(int s) { closelog(); exit(0); }

static int daemonize(void)
{
    pid_t pid;
    int fd;

    if((pid = fork()) < 0){ perror("fork()"); return -1; }
    if(pid > 0){ exit(0); }
    if((fd = open("/dev/null", O_RDWR)) < 0){ perror("open()"); return -2; }
    else{
        dup2(fd, 0);
        dup2(fd, 1);
        dup2(fd, 2);
        if(fd > 2){ close(fd); }
    }
    setsid();
    chdir("/");
    umask(0);
    return 0;
}

static int socket_init(void)
{
    struct ip_mreqn mreq;

    serversd = socket(AF_INET, SOCK_DGRAM, 0);
    if(serversd < 0){ syslog(LOG_ERR, "socket():%s", strerror(errno)); exit(1); }

    //创建多拨组
    inet_pton(AF_INET, server_conf.mgroup, &mreq.imr_multiaddr);/* if 1 succeed else if(0||-1) error */
    inet_pton(AF_INET, "0.0.0.0", &mreq.imr_address);/* if 1 succeed else if(0||-1) error */
    mreq.imr_ifindex = if_nametoindex(server_conf.ifname); //网络设备索引号
    if(setsockopt(serversd, IPPROTO_IP, IP_MULTICAST_IF, &mreq, sizeof(mreq)) < 0){
        syslog(LOG_ERR, "setsockopt(IP_MULTICAST_IF):%s", strerror(errno)); exit(1);
    }

    //bind(); //省略

    sndaddr.sin_family = AF_INET;
    sndaddr.sin_port = htons(atoi(server_conf.rcvport));
    inet_pton(AF_INET, server_conf.mgroup, &sndaddr.sin_addr.s_addr);
    return 0;
}

int main(int argc, char **argv)
{
    int c;
    struct sigaction sa;

    sa.sa_handler = daemon_exit;
    sigemptyset(&sa.sa_mask);
    sigaddset(&sa.sa_mask, SIGINT);
    sigaddset(&sa.sa_mask, SIGQUIT);
    sigaddset(&sa.sa_mask, SIGTERM);
    sigaction(SIGINT, &sa, NULL);
    sigaction(SIGQUIT, &sa, NULL);
    sigaction(SIGTERM, &sa, NULL);

    openlog("netradio", LOG_PID|LOG_PERROR, LOG_DAEMON);

    //分析命令行
    while((c = getopt(argc, argv, "P:M:D:FI:H")) > 0){
        switch(c){
            case '?': usage(); exit(1); break;
            case 'P': server_conf.rcvport = optarg; break;
            case 'M': server_conf.mgroup = optarg; break;
            case 'D': server_conf.media_dir = optarg; break;
            case 'F': server_conf.runmode = RUN_FOREGROUND; break;
            case 'I': server_conf.ifname = optarg; break;
            case 'H': usage(); exit(0); break;
            default: abort(); break;
        }
    }

    //守护进程
    if(server_conf.runmode == RUN_DAEMON){ if(daemonize() < 0){ exit(1); } }
    else if(server_conf.runmode == RUN_FOREGROUND){ /* do STH */ }
    else{ fprintf(stderr, "EINVAL: server_conf.runmode\n"); exit(1); }

    //socket
    socket_init();

    //获取频道信息
    struct mlib_listentry_st *list;
    int list_size;
    int err;
    if((err = mlib_getchnlist(&list, &list_size))){ syslog(LOG_ERR, "mlib_getchnlist():%s", strerror(err)); exit(1); }

    //创建节目单线程
    if((err = thr_list_creat(list, list_size))){ /*syslog(LOG_ERR, "thr_list_creat():%s", strerror(err));*/ exit(1); }

    //创建频道线程
    for(i = 0; i < list_size; i++)
    {
        thr_channel_creat(list+i);
        //if error
    }

    syslog(LOG_DEBUG, "%d channel threads created.", i);
    while(1){ pause(); }
    return 0;
}
